home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mm / mm-0.90 / seq.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-18  |  26.4 KB  |  1,069 lines

  1. /*
  2.  * Copyright (c) 1986, 1990 by The Trustees of Columbia University in
  3.  * the City of New York.  Permission is granted to any individual or
  4.  * institution to use, copy, or redistribute this software so long as it
  5.  * is not sold for profit, provided this copyright notice is retained.
  6.  */
  7.  
  8. #ifndef lint
  9. static char *rcsid = "$Header: /f/src2/encore.bin/cucca/mm/tarring-it-up/RCS/seq.c,v 2.2 90/10/04 18:26:32 melissa Exp $";
  10. #endif
  11.  
  12. /*
  13.  * seq.c - message sequence selection routines
  14.  *
  15.  *
  16.  * Notes:
  17.  *
  18.  * Sequence constraint minima are inclusive and maxima are exclusive.  For
  19.  * example, a 2000 byte message received "today" is "longer (than) 2000" and
  20.  * "since today" but not "shorter (than) 2000" or "before today".
  21.  */
  22.  
  23. #include "mm.h"
  24. #include "parse.h"
  25.  
  26. #define CS (cf->sequence)
  27. #define PS (cf->prev_sequence)
  28. #define RS (cf->read_sequence)
  29.  
  30. #define CSfirst (sequence_first(CS))
  31. #define CSlast (sequence_last(CS))
  32. #define CSinverted (sequence_inverted(CS))
  33. #define PSfirst (sequence_first(PS))
  34. #define PSlast (sequence_last(PS))
  35. #define PSinverted (sequence_inverted(PS))
  36. #define RSfirst (sequence_first(RS))
  37. #define RSlast (sequence_last(RS))
  38. #define RSinverted (sequence_inverted(RS))
  39.  
  40. /*
  41.  * message selection keywords
  42.  */
  43.  
  44. keywrd seq_keys[] = {
  45.     { "a", KEY_INV|KEY_ABR, 2 },    /* a --> all */
  46.     { "after", 0, (keyval) SEQ_AFTER },
  47.     { "all", 0, (keyval) SEQ_ALL },
  48.     { "answered", 0, (keyval) SEQ_ANSWERED },
  49.     { "before", 0, (keyval) SEQ_BEFORE },
  50.     { "current", 0, (keyval) SEQ_CURRENT },
  51.     { "deleted", 0, (keyval) SEQ_DELETED },
  52.     { "flagged", 0, (keyval) SEQ_FLAGGED },
  53.     { "from", 0, (keyval) SEQ_FROM },
  54.     { "inverse", 0, (keyval) SEQ_INVERSE },
  55.     { "keyword", 0, (keyval) SEQ_KEYWORD },
  56.     { "l", KEY_INV|KEY_ABR, (keyval) 12 }, /* 12 --> last (groan) */
  57.     { "last", 0, (keyval) SEQ_LAST },
  58.     { "longer", 0, (keyval) SEQ_LONGER },
  59.     { "new", 0, (keyval) SEQ_NEW },
  60.     { "on", 0, (keyval) SEQ_ON },
  61.     { "previous-sequence", 0, (keyval) SEQ_PREVIOUS },
  62.     { "recent", 0, (keyval) SEQ_RECENT },
  63.     { "seen", 0, (keyval) SEQ_SEEN },
  64.     { "shorter", 0, (keyval) SEQ_SHORTER },
  65.     { "since", 0, (keyval) SEQ_SINCE },
  66.     { "subject", 0, (keyval) SEQ_SUBJECT },
  67.     { "text", 0, (keyval) SEQ_TEXT },
  68.     { "to", 0, (keyval) SEQ_TO },
  69.     { "u", KEY_ABR|KEY_INV, (keyval) 29 }, /* u -> unseen */
  70.     { "unanswered", 0, (keyval) SEQ_UNANSWERED },
  71.     { "undeleted", 0, (keyval) SEQ_UNDELETED },
  72.     { "unflagged", 0, (keyval) SEQ_UNFLAGGED },
  73.     { "unkeyword", 0,  (keyval) SEQ_UNKEYWORD },
  74.     { "unseen", 0, (keyval) SEQ_UNSEEN }
  75. };
  76.  
  77. seq_node seq_stack[SEQ_STACK_SIZE];
  78. int seq_stackp;
  79. #define stackinit() seq_stackp = -1, invert_sequence = 0, use_previous = 0
  80. #define stacktop seq_stack[seq_stackp]
  81. #define stackdata stacktop.data
  82. #define stackrange stackdata.range
  83. #define stackdates stackdata.dates
  84. #define stackpush(x) if (++seq_stackp >= sizeof seq_stack/sizeof seq_stack[0])\
  85.             ccmd_errmsg("Too many sequence constraints");\
  86.              else\
  87.             seq_stack[seq_stackp].type = x;
  88. #define stackpop() seq_stackp--
  89. static int invert_sequence, use_previous;
  90.  
  91. int seq_date (), seq_num (), seq_string (), seq_misc(), seq_int(), 
  92.     seq_keyword();
  93.  
  94. int (*seq_fns[sizeof(seq_keys)/sizeof(seq_keys[0])])() = { 
  95.     seq_date,                /* after */
  96.     seq_misc,                /* all */
  97.     seq_misc,                /* answered */
  98.     seq_date,                /* before */
  99.     seq_misc,                /* current */
  100.     seq_misc,                /* deleted */
  101.     seq_misc,                /* flagged */
  102.     seq_string,                /* from */
  103.     seq_misc,                /* inverse */
  104.     seq_keyword,            /* keyword */
  105.     seq_int,                /* last */
  106.     seq_num,                /* longer */
  107.     seq_misc,                /* new */
  108.     seq_date,                /* on */
  109.     seq_misc,                /* previous-sequence */
  110.     seq_misc,                /* recent */
  111.     seq_misc,                /* seen */
  112.     seq_num,                /* shorter */
  113.     seq_date,                /* since */
  114.     seq_string,                /* subject */
  115.     seq_string,                /* text */
  116.     seq_string,                /* to */
  117.     seq_misc,                /* unanswered */
  118.     seq_misc,                /* undeleted */
  119.     seq_misc,                /* unflagged */
  120.     seq_keyword,            /* unkeyworded */
  121.     seq_misc                /* unseen */
  122. };
  123.  
  124. keytab seq_keytab = { sizeof (seq_keys) / sizeof (keywrd), seq_keys };
  125.  
  126. fdb seq_fdb_key = 
  127.     { _CMKEY, 0, 0, (pdat) &seq_keytab, "message sequence, ", NULL, NULL, 
  128.       "invalid message sequence type"};
  129. fdb seq_fdb_n =
  130.     { _CMNUM, CM_SDH, 0, (pdat) 10, "message number\n\
  131.   or range of message numbers, n:m\n\
  132.   or range of message numbers, n-m\n\
  133.   or range of message numbers, n+m (m messages beginning with n)",
  134.     NULL, NULL, "Invalid message sequence" };
  135. fdb seq_fdb_dot =
  136.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) ".",
  137.       "\".\" to specify the current message" };
  138. fdb seq_fdb_star =
  139.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) "*",
  140.       "\"*\" to specify the last message" };
  141. fdb seq_fdb_percent =
  142.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) "%", NULL };
  143. fdb seq_fdb_colon = 
  144.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) ":",
  145.       "\":\" to specify a message range",
  146.       NULL, NULL, "Invalid message range delimiter"};
  147. fdb seq_fdb_dash =
  148.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) "-",
  149.       "\"-\" to specify a message range",
  150.       NULL, NULL, "Invalid sequence range delimiter"};
  151. fdb seq_fdb_hash =
  152.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) "#", NULL , NULL, NULL,
  153.       "Invalid message range delimiter" };
  154. fdb seq_fdb_plus =
  155.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) "+",
  156.       "\"+\" to specify a message range",
  157.       NULL, NULL, "Invalid sequence range delimiter" };
  158. fdb seq_fdb_comma =
  159.     { _CMTOK, CM_SDH|TOK_WAK, 0, (pdat) ",",
  160.       "\",\" and another message sequence", 
  161.       NULL, NULL, "Expected a \",\""};
  162. fdb seq_fdb_timeonly =
  163.     { _CMTAD, CM_SDH|DTP_N24|DTP_NDA|DTP_NDW|DTP_NSP, nil, nil,
  164.       "date and time", NULL, NULL, "Invalid date and time" };
  165. fdb seq_fdb_dateonly =
  166.     { _CMTAD, CM_SDH|DTP_N24|DTP_NTI|DTP_NDW|DTP_NSP, nil, nil,
  167.       "date and time", NULL, NULL, "Invalid date" };
  168. fdb seq_fdb_tad = { _CMTAD, CM_SDH|DTP_N24|DTP_NDW|DTP_NSP };
  169. extern keytab timetab;
  170. fdb seq_fdb_daykey = { _CMKEY, CM_SDH, nil, (pdat) &timetab, "day, ", NULL,
  171.                NULL, "Invalid day" };
  172.  
  173. #define SEQ_FDBS &seq_fdb_n, &seq_fdb_dot, &seq_fdb_star, &seq_fdb_key, \
  174.     &seq_fdb_percent, &seq_fdb_comma
  175. #define SEQ_TAD_FDBS &seq_fdb_tad, &seq_fdb_dateonly, &seq_fdb_timeonly, \
  176.     &seq_fdb_daykey
  177.  
  178. /* miscellaneous easy sequence functions */
  179. seq_keyword(n)
  180. {
  181.     stackpush(n);
  182.     strcpy(stackdata.s,parse_old_keywords());
  183.     parse_seq ((char *) nil, nil, nil);
  184. }
  185.     
  186.  
  187. seq_misc(n)
  188. {
  189.     stackpush(n);
  190.     switch (n) {
  191.       case SEQ_PREVIOUS:
  192.     use_previous = true;
  193.     break;
  194.       case SEQ_INVERSE:
  195.     invert_sequence = !invert_sequence; /* XXX should be a seq_node */
  196.     break;
  197.       case SEQ_ALL:
  198.       case SEQ_ANSWERED:
  199.       case SEQ_CURRENT:
  200.       case SEQ_DELETED:
  201.       case SEQ_FLAGGED:
  202.       case SEQ_NEW:
  203.       case SEQ_RECENT:
  204.       case SEQ_SEEN:
  205.       case SEQ_UNANSWERED:
  206.       case SEQ_UNDELETED:
  207.       case SEQ_UNFLAGGED:
  208.       case SEQ_UNSEEN:
  209.     break;                /* XXX get rid of these */
  210.       default:
  211.     ccmd_errmsg ("Unknown sequence constraint (%d) in seq_misc", n);
  212.     }
  213.     parse_seq ((char *) nil, nil, nil);
  214. }
  215.  
  216. seq_date (n)
  217. {
  218.     fdb *fdbs;
  219.  
  220.     stackpush(n);
  221.  
  222.     fdbs = fdbchn (&seq_fdb_daykey, &seq_fdb_dateonly, (fdb*) nil);
  223.     parse (fdbs, &pv, &used);
  224.  
  225.     switch (used->_cmfnc) {
  226.       case _CMKEY:
  227.     stackdates.first = key2time(pv._pvint);
  228.     break;
  229.       case _CMTAD:
  230.     stackdates.first = datimetogmt (&pv._pvtad);
  231.     break;
  232.       default:
  233.     ccmd_errmsg ("Bad parse function (%d) in seq_date", used->_cmfnc);
  234.     }
  235.  
  236.     /* got date, now look for additional time field or something else */
  237.     fdbs = fdbchn (&seq_fdb_timeonly, SEQ_FDBS, &cfm_fdb, (fdb *) nil);
  238.     parse (fdbs, &pv, &used);
  239.     if (n == SEQ_ON)
  240.     stackdates.last = stackdates.first + (24 * 60 * 60);
  241.     if ((n == SEQ_AFTER) && (used->_cmfnc != _CMTAD))
  242.     stackdates.first += (24*60*60);
  243.     switch (used->_cmfnc) {
  244.       case _CMCFM:
  245.     return true;
  246.       case _CMTAD:
  247.     stackdates.first += (((pv._pvtad._dthr*60)+pv._pvtad._dtmin)*60) +
  248.         pv._pvtad._dtsec;
  249.     return parse_seq ((char *) nil, nil, nil);
  250.       default:
  251.     return seq_interpret_fdb(used);
  252.     }
  253. }
  254.  
  255. seq_comma (comma_seen)
  256. int comma_seen;
  257. {
  258.     if (!comma_seen)
  259.     if (parse (fdbchn (&seq_fdb_comma, &cfm_fdb, nil), &pv, &used) ==
  260.         CMxOK)
  261.         switch (used->_cmfnc) {
  262.           case _CMCFM:
  263.         return true;
  264.           case _CMTOK:
  265.         break;
  266.         }
  267.     parse_seq ("", nil, nil);
  268.     return true;
  269. }
  270.  
  271. seq_num (n)
  272. {
  273.     noise ("than");
  274.     p_num ("number of characters");
  275.     stackpush(n);
  276.     stackrange.n = pv._pvint;
  277.     parse_seq (nil, nil, nil);
  278. }
  279.  
  280. seq_int (n)
  281. {
  282.     static fdb numfdb = { _CMNUM, CM_SDH, 0, (pdat) 10, NULL, NULL, NULL,
  283.               "invalid number" };
  284.     numfdb._cmhlp = "decimal number";
  285.     numfdb._cmdef = "1";
  286.  
  287.     noise("number of messages");
  288.     parse(&numfdb, &pv, &used);
  289.     stackpush(n);
  290.     stackrange.n = pv._pvint;
  291.     parse_seq (nil, nil, nil);
  292. }
  293.  
  294. seq_string (n)
  295. {
  296.     extern brktab rembrk;        /* remote user name break table */
  297.     static fdb string_fdb = { _CMFLD, CM_SDH|FLD_EMPTY|FLD_WAK, nil, nil,
  298.                   "string", NULL, &rembrk, "Invalid String" };
  299.     static fdb qst_fdb = { _CMQST, nil, nil, nil, nil, nil, nil,
  300.                    "Invalid String" };
  301.     int len;
  302.  
  303.     noise ("string");
  304.     parse (fdbchn (&qst_fdb, &string_fdb, (fdb *) nil), &pv, &used);
  305.     if ((len = strlen (atmbuf)) < 1)
  306.     ccmd_errmsg ("Invalid string.  Try enclosing it in quotes");
  307.     if (len > (sizeof stackdata.s - 1))
  308.     ccmd_errmsg ("String too long - \"%s\"", atmbuf);
  309.     stackpush(n);
  310.     strcpy (stackdata.s, atmbuf);
  311.     return parse_seq ((char *) nil, nil, nil);
  312. }
  313.  
  314. seq_range (n)
  315. int n;
  316. {
  317.     fdb *fdbs = fdbchn (&seq_fdb_colon, &seq_fdb_dash, &seq_fdb_plus,
  318.             &seq_fdb_hash, &seq_fdb_comma, &cfm_fdb, nil);
  319.  
  320.     if (n < 1 || n > cf->count)
  321.     ccmd_errmsg ("Number out of range");
  322.     stackpush(SEQ_RANGE);
  323.     stackrange.n = stackrange.m = n;
  324.     if (parse (fdbs, &pv, &used) == CMxOK) {
  325.     if (used == &cfm_fdb) {
  326.         return true;        /* return just 1 msg num */
  327.     }
  328.     if (used->_cmfnc == _CMTOK)
  329.         switch (used->_cmdat[0]) {
  330.           case ',':
  331.         parse_seq ("", nil, nil);
  332.         return true;
  333.         break;
  334.           case '+':
  335.           case '#':
  336.         stackrange.m += p_num ("number of messages") - 1;
  337.         break;
  338.           case '-':
  339.           case ':':
  340.         {
  341.             static fdb numfdb = { _CMNUM, CM_SDH, 0, (pdat) 10,
  342.                           "end of range", NULL, NULL,
  343.                           "bad range end" };
  344.             parse (fdbchn (&numfdb, &seq_fdb_star, &seq_fdb_percent, 
  345.                    nil), &pv, &used);
  346.             stackrange.m = ((used == &seq_fdb_star) ||
  347.                     (used == &seq_fdb_percent)) ?
  348.                       cf->count : pv._pvint;
  349.         }            
  350.         break;
  351.           default:
  352.         ccmd_errmsg ("Internal error in token evaluation");
  353.         }
  354.     if (stackrange.m < stackrange.n)
  355.         ccmd_errmsg ("Invalid range");
  356. /*    seq_comma (nil);*/
  357.     parse_seq (nil, nil, nil);
  358.     return true;
  359.     }
  360.     else
  361.     return false;
  362. }
  363.  
  364. /*
  365.  * chain together various pieces for a sequence parse
  366.  */
  367. fdb *
  368. build_seq_chn (chn, def, chnlen, realseq)
  369. fdb *chn;
  370. char *def;
  371. int *chnlen;
  372. int realseq;                /* sequence or confirm */
  373. {
  374.     fdb *f;
  375.     
  376.     *chnlen = 0;
  377.  
  378.     (*chnlen)++;            /* at least one element */
  379.  
  380.     /* chain the normal stuff at the end of this chain */
  381.     for (f = chn; f->_cmlst != nil; f = f->_cmlst)
  382.     (*chnlen)++;
  383.     /* f now points to the last element of the chain */
  384.     if (!realseq)            /* just stick in a confirm */
  385.         fdbchn (f, &cfm_fdb, (fdb *) nil);
  386.     else if (def)             /* non-nil def means required input */
  387.     fdbchn (f, SEQ_FDBS, (fdb *) nil);
  388.     else
  389.     fdbchn (f, SEQ_FDBS, &cfm_fdb, (fdb *) nil);
  390.     return (chn);
  391. }
  392.  
  393. /*
  394.  * You want parse_sequence from outside this module...
  395.  *
  396.  * recursively parse a message sequence.  if def is non-null, a response
  397.  * will be required; if the string it points to isn't null, use that as a
  398.  * default string.  Thus:
  399.  *
  400.  *     parse_seq (nil, ...);        parse sequence or confirm
  401.  *     parse_seq ("", ...);        parse required sequence
  402.  *     parse_seq ("all", ...);        parse sequence, confirm -> all
  403.  */
  404.  
  405. static
  406. parse_seq (def, chn, altused)
  407. char *def;
  408. fdb *chn;
  409. fdb **altused;
  410. {
  411.     fdb *fdbs, *f;
  412.     int l = 0;
  413.     int i;
  414.  
  415.     seq_fdb_n._cmdef = (def && *def) ? def : nil; /* XXX */
  416.  
  417.     if (!chn) {
  418.     if (def)            /* non-nil def means required input */
  419.         fdbs = fdbchn (SEQ_FDBS, (fdb *) nil);
  420.     else
  421.         fdbs = fdbchn (SEQ_FDBS, &cfm_fdb, (fdb *) nil);
  422.     }
  423.     else
  424.     fdbs = build_seq_chn (chn, def, &l, TRUE); /* chain extras in front */
  425.  
  426.     parse (fdbs, &pv, &used);    /* parse the first token */
  427.     seq_fdb_n._cmdef = nil;        /* XXX */
  428.     for (i = 0, f = chn; i < l; i++, f = f->_cmlst) {
  429.     if (used == f) {        /* one of the chain elements */
  430.         *altused = used;
  431.         return;
  432.     }
  433.     }
  434.  
  435.     /* otherwise, it was really a sequence parse */
  436.     seq_interpret_fdb (used);
  437. }
  438.  
  439. /*
  440.  * parse a message sequence and save the result as the "current sequence"
  441.  *
  442.  *     parse_sequence (nil, ...);    parse sequence or confirm
  443.  *     parse_seqeunce ("", ...);    parse required sequence
  444.  *     parse_sequence ("all", ...);    parse sequence, confirm -> all
  445.  *
  446.  * returns false if changes the message sequence is prohibited by the
  447.  * current context, true otherwise.
  448.  *
  449.  * If an fdb chain is passed, link this to the front of our sequence.
  450.  * After parsing, fill in used with the fdb we parsed if it was one of the
  451.  * passed ones (pval in global pv), or NULL if we parsed a sequence.
  452.  * This is done so optional items can be parsed before a sequence.
  453.  * (chn should be NULL iff altused is)
  454.  *
  455.  */
  456.  
  457. int
  458. parse_sequence (def, chn, altused)
  459. char *def;                /* default response */
  460. fdb *chn;
  461. fdb **altused;
  462. {
  463.     int i, l;
  464.     sequence_t temp;
  465.  
  466.     /*
  467.      * MM-20 compatibility: don't allow user to change the message
  468.      * sequence in the middle of a read or send command.  We can
  469.      * relax this later, once we redefine the semantics of "previous-sequence".
  470.      */
  471.  
  472.     if (altused)
  473.     *altused = NULL;        /* no fdb yet */
  474.  
  475.     if (mode != MM_TOP_LEVEL) {
  476.     if (chn) {            /* parse optional stuff */
  477.         parse (build_seq_chn (chn, def, &l, FALSE), &pv, &used);
  478.         if (used != &cfm_fdb)
  479.         *altused = used;
  480.     }
  481.     else
  482.         confirm();
  483.     return false;
  484.     }
  485.  
  486.     noise ("messages");
  487.  
  488.     stackinit();
  489.  
  490.     parse_seq (def, chn, altused);    /* do the actual work */
  491.     if (altused && *altused)        /* sequence not parsed */
  492.     return true;
  493.  
  494.     /*
  495.      * Save the current sequence as the "previous-sequence", using
  496.      * a swap to avoid free and malloc.
  497.      */
  498.     temp = PS;
  499.     PS = CS;
  500.     CS = temp;
  501.     
  502.     clear_sequence (CS);
  503.     
  504.     for (i = 1; i <= cf->count; i++) {
  505.     if (use_previous) {
  506.         if (i < PSfirst)
  507.         i = PSfirst;
  508.         else if (i > PSlast)
  509.         break;
  510.         if (!in_sequence (PS, i))
  511.         continue;
  512.     }
  513.     if (!seq_test (i))
  514.         continue;
  515.     setbit (sequence_bits(CS), i);
  516.     if (CSfirst == 0)
  517.         CSfirst = i;
  518.     CSlast = i;
  519.     }
  520.     if (CSlast < CSfirst)        /* sequence is empty */
  521.     CSlast = CSfirst = 0;
  522.     
  523.     sequence_inverted(CS) = invert_sequence;
  524.     if (use_previous && sequence_inverted(PS))
  525.     sequence_inverted(CS) = !sequence_inverted(CS);
  526.     if (debug)
  527.     fprintf (stderr, "sequence: first = %d, last = %d, current = %d\n",
  528.          CSfirst, CSlast, cf->current);
  529.     return true;
  530. }
  531.  
  532. /*
  533.  * sequence_init - allocate and initialize sequence bitmaps
  534.  *
  535.  */
  536.  
  537. sequence_init (cf)
  538. msgvec *cf;
  539. {
  540.     int n = cf->count / NBBY + 1;
  541.     if ((CS = (sequence_t) malloc (sizeof (*CS))) &&
  542.     (sequence_bits (CS) = (unsigned char *) malloc (n)) &&
  543.     (PS = (sequence_t) malloc (sizeof (*PS))) &&
  544.     (sequence_bits (PS) = (unsigned char *) malloc (n)) &&
  545.     (RS = (sequence_t) malloc (sizeof (*RS))) &&
  546.     (sequence_bits (RS) = (unsigned char *) malloc (n))) {
  547.  
  548.     sequence_file(CS) = cf;
  549.     sequence_file(PS) = cf;
  550.     sequence_file(RS) = cf;
  551.     
  552.     clear_sequence (CS);
  553.     clear_sequence (PS);
  554.     clear_sequence (RS);
  555.     return true;
  556.     }
  557.     return false;
  558. }
  559.  
  560. sequence_free (cf)
  561. msgvec *cf;
  562. {
  563.     free_sequence(CS);
  564.     free_sequence(PS);
  565.     free_sequence(RS);
  566. }
  567.  
  568. /*
  569.  * Make sequence S1 a copy of sequence S2.
  570.  */
  571. copy_sequence (S1, S2)
  572. sequence_t S1, S2;
  573. {
  574.     sequence_first (S1) = sequence_first (S2);
  575.     sequence_last (S1) = sequence_last (S2);
  576.     sequence_inverted (S1) = sequence_inverted (S2);
  577.     sequence_file (S1) = sequence_file (S2);
  578.     bcopy (sequence_bits(S2), sequence_bits(S1),
  579.        sequence_last (S1) / NBBY + 1);
  580. }
  581.  
  582. /*
  583.  * set the current message number, and return it.  The result will be
  584.  * zero if the message sequence is empty.
  585.  */
  586. int
  587. sequence_start (S)
  588. sequence_t S;
  589. {
  590.     if (!valid_sequence(S))
  591.     return 0;
  592.     return (sequence_file(S)->current =
  593.         sequence_inverted(S) ? sequence_last(S) : sequence_first(S));
  594. }
  595.  
  596. /* XXX make these macros later on */
  597. int
  598. sequence_next (S)
  599. sequence_t S;
  600. {
  601.     return sequence_next1 (S, 1);
  602. }
  603.  
  604. int
  605. sequence_prev (S)
  606. sequence_t S;
  607. {
  608.     return sequence_next1 (S, -1);
  609. }
  610.  
  611. /*
  612.  * advance to the next message in sequence S.
  613.  * dir is 1 to move forward, -1 to move backards.
  614.  */
  615. sequence_next1 (S, dir)
  616. sequence_t S;
  617. int dir;
  618. {
  619.     int n;
  620.     if (!valid_sequence(S))
  621.     return 0;
  622.  
  623.     n = sequence_file(S)->current;    /* get current message */
  624.  
  625.     if (sequence_inverted(S))        /* change direction if inverse seq */
  626.     dir = -dir;
  627.  
  628.     while ((n += dir) && (n <= sequence_last(S)))
  629.     if (in_sequence (S, n))
  630.         return (sequence_file(S)->current = n);
  631.     return 0;
  632. }
  633.  
  634.  
  635. /*
  636.  * find the next message in the sequence
  637.  * arg is +1 to move up in the sequence, and -1 to move back
  638.  * OBSOLETE
  639.  */
  640.  
  641. int
  642. next_current (up)            /* was next_current */
  643. {
  644.     int n = cf->current;
  645.  
  646.     up = (up < 0) ? -1 : 1;
  647.     if (CSinverted)
  648.     up = -up;
  649.  
  650.     /* XXX this could be a lot faster! */
  651.     while ((n += up) && (n > 0) && (n <= CSlast))
  652.     if (in_sequence (CS, n)) {
  653.         cf->current = n;
  654.         return n;
  655.     }
  656.     return 0;
  657. }
  658.  
  659.  
  660. int
  661. seq_next (n)
  662. int n;
  663. {
  664.     if (CSinverted)
  665.     if (n < 1)
  666.         return CSlast;
  667.     else
  668.         while (n-- > CSlast)
  669.         if (in_sequence (CS, n))
  670.             return n;
  671.     else
  672.     if (n < 1)
  673.         return CSfirst;
  674.     else
  675.         while (n++ < CSlast)
  676.         if (in_sequence (CS, n))
  677.             return n;
  678.     return 0;
  679. }
  680.  
  681. int
  682. seq_print (nl)
  683. {
  684.     int lastn, lastprinted;
  685.     int n = sequence_start (cf->sequence);
  686.  
  687.     if (!n)
  688.     return (0);
  689.  
  690.     printf (" %d", lastprinted = n);
  691.  
  692.     lastn = 0;                /* initialize this */
  693.     while (n = sequence_next (cf->sequence)) {
  694.     if (n == (lastprinted + 1)) {
  695.         lastn = lastprinted = n;
  696.         continue;
  697.     }
  698.     if (lastn) {
  699.         printf (":%d", lastn);
  700.         lastn = 0;
  701.     }
  702.     printf (", %d", lastprinted = n);
  703.     }
  704.     if (lastn)
  705.     printf (":%d", lastn);
  706.     if (nl)
  707.     printf ("\n");
  708.     return 1;
  709. }
  710.  
  711. seq_interpret_fdb (used)
  712. fdb *used;
  713. {
  714.     int n;
  715.  
  716.     switch (used->_cmfnc) {
  717.       case _CMCFM:
  718.     return true;
  719.       case _CMKEY:
  720.     n = pv._pvint;
  721.     if (seq_fns[n])
  722.         (*seq_fns[n]) (n);        /* call handler function */
  723.     else
  724.         parse_seq ((char *) nil, nil, nil);
  725.     break;
  726.       case _CMTOK:
  727.     switch (used->_cmdat[0]) {
  728.       case '.':
  729.         if (seq_range (cf->current)) /* XXX */
  730.         return true;
  731.         break;
  732.       case '*':
  733.       case '%':
  734.         if (seq_range (cf->count))    /* XXX */
  735.         return true;
  736.         break;
  737.       case ',':
  738.         parse_seq ("", nil, nil);
  739.         return true;
  740.       default:
  741.         ccmd_errmsg ("Invalid token '%s' parsed", used->_cmdat);
  742.     }
  743.     break;
  744.       case _CMNUM:
  745.     if (seq_range (pv._pvint))
  746.         return true;
  747.     seq_comma (true);
  748.     break;
  749.       default:
  750.     ccmd_errmsg ("Got an unexpected fdb in seq_interpret_fdb (%d)",
  751.            used->_cmfnc);
  752.     }
  753. }
  754.  
  755. FILE *header_pipe = NULL;
  756.  
  757. int
  758. cmd_headers(n)
  759. int n;
  760. {
  761.     void header_print();
  762.     FILE *more_pipe_open();
  763.  
  764.     if (!check_cf (O_RDONLY))
  765.     return;
  766.     header_pipe = cmcsb._cmoj ? cmcsb._cmoj : stdout; /* don't write to NULL */
  767.     if (parse_sequence ("current", nil, nil)) {    /* count how many headers */
  768.     int count,n;
  769.     for (count = 0, n = sequence_start (cf->sequence); 
  770.          n && count < cmcsb._cmrmx;
  771.          n = sequence_next (cf->sequence))
  772.         ++count;
  773.     if (count >= cmcsb._cmrmx)
  774.         header_pipe = more_pipe_open(header_pipe);
  775.     }
  776.     sequence_loop (header_print);
  777.     if ((header_pipe == cmcsb._cmoj) ||
  778.     (header_pipe == stdout))    /* didn't open any pipe */
  779.     fflush(header_pipe);        /* ... since we're not closing it */
  780.     else
  781.     more_pipe_close(header_pipe);
  782.     header_pipe = NULL;
  783. }
  784.  
  785. void
  786. header_print(n)
  787. {
  788.     static header_count = 0;
  789.     extern int header_summary ();
  790.  
  791.     switch (n) {
  792.       case 0:
  793.     header_count = 0;
  794.     break;
  795.       case -1:
  796.     if (debug)
  797.         printf ("Matching headers = %d\n", header_count);
  798.     break;
  799.       default:
  800.     header_count++;
  801.     header_summary (n, header_pipe, 0); /* 0 => "header command" */
  802.     }
  803. }
  804.  
  805. /*
  806.  * Apply a function to each message in the current message sequence.
  807.  * 
  808.  * In addition to valid message numbers, the function is also called
  809.  * with zero to indicate the beginning of a sequence, and -1 to indicate
  810.  * that the sequence has been exhausted.
  811.  */
  812.  
  813. sequence_loop (fn)
  814. void (*fn)();
  815. {
  816.     int i;
  817.     int new = 1;
  818.  
  819.     (*fn) (0);                /* initialize the function */
  820.  
  821.     if (mode == MM_TOP_LEVEL) {
  822.     sequence_t S = CS;        /* use current sequence */
  823.  
  824.     if (valid_sequence (S)) {
  825.         if (sequence_inverted (S))    /* run backwards */
  826.         for (i = sequence_last (S); i >= sequence_first (S); i--) {
  827.             if (in_sequence (S, i)) {
  828.             cf->current = i;
  829.             (*fn) (i);
  830.             }
  831.         }
  832.         else            /* run forwards */
  833.         for (i = sequence_first(S); i <= sequence_last(S); i++)
  834.             if (in_sequence (S, i)) {
  835.             cf->current = i;
  836.             (*fn) (i);
  837.             }
  838.     }
  839.     }
  840.     else
  841.     if (cf->current)
  842.         (*fn) (cf->current);    /* just use current message */
  843.  
  844.     (*fn) (-1);                /* tell function we're done */
  845. }
  846.  
  847. /*
  848.  * Test whether message n is part of the user's selection.
  849.  * This is pretty awful, but it'll do until we finish defining
  850.  * the message sequence grammar (at which point we'll need an
  851.  * and/or tree anyway).
  852.  */
  853.  
  854. seq_test (n)
  855. int n;                    /* message number */
  856. {
  857.     int i, inrange = false, haverange = false;
  858.     
  859.     /*
  860.      * We test ranges first as a (lame) optimization.
  861.      * Obviously this could be a lot faster.
  862.      */
  863.  
  864.     for (i = 0; i <= seq_stackp; i++)
  865.     if (seq_stack[i].type == SEQ_RANGE) {
  866.         haverange = true;
  867.         if (seq_test1 (n, &seq_stack[i]) == false)
  868.         continue;
  869.         inrange = true;
  870.         break;
  871.     }
  872.  
  873.     if (haverange && !inrange)
  874.     return false;
  875.  
  876.     for (i = 0; i <= seq_stackp; i++)
  877.     if (seq_stack[i].type != SEQ_RANGE)
  878.         if (seq_test1 (n, &seq_stack[i]) == false)
  879.         return false;
  880.  
  881.     return true;
  882. }
  883.  
  884. static
  885. seq_test1(n, node)
  886. int n;
  887. seq_node *node;
  888. {
  889.     message *m = &cf->msgs[n];
  890.     switch (node->type) {
  891.       case SEQ_SINCE:
  892.       case SEQ_AFTER:
  893.     return (m->date >= node->data.dates.first);
  894.       case SEQ_BEFORE:
  895.     return (m->date < node->data.dates.first);
  896.       case SEQ_INVERSE:
  897.       case SEQ_ALL:
  898.     return true;        /* XXX should not get here */
  899.       case SEQ_ANSWERED:
  900.     return (m->flags & M_ANSWERED);
  901.       case SEQ_CURRENT:
  902.     return (cf->current == n);
  903.       case SEQ_DELETED:
  904.     return (m->flags & M_DELETED);
  905.       case SEQ_FLAGGED:
  906.     return (m->flags & M_FLAGGED);
  907.       case SEQ_FROM:
  908.     if (search_header ("from", node->data.s, m))
  909.         return true;
  910.     return false;
  911.       case SEQ_KEYWORD:
  912.     return(lookup_keyword(node->data.s, m->keywords));
  913.       case SEQ_LAST:
  914.     return (cf->count < n + node->data.range.n);
  915.       case SEQ_SHORTER:
  916.     return (m->size < node->data.range.n);
  917.       case SEQ_LONGER:
  918.     return (m->size >= node->data.range.n);
  919.       case SEQ_NEW:
  920.     return (!(m->flags & M_SEEN) && (m->flags & M_RECENT));
  921.       case SEQ_ON:
  922.     return (m->date >= node->data.dates.first &&
  923.         m->date <= node->data.dates.last);
  924.       case SEQ_PREVIOUS:
  925.     return true;            /* XXX caller has to check... */
  926.       case SEQ_RECENT:
  927.     return (m->flags & M_RECENT);
  928.       case SEQ_SEEN:
  929.     return (m->flags & M_SEEN);
  930.       case SEQ_SUBJECT:
  931.     if (search_header ("subject", node->data.s, m))
  932.         return true;
  933.     return false;
  934.       case SEQ_TEXT:
  935.     if (search_text (node->data.s, m))
  936.         return true;
  937.     return false;
  938.       case SEQ_TO:
  939.     if ((search_header ("to", node->data.s, m)) ||
  940.         (search_header ("cc", node->data.s, m)) ||
  941.         (search_header ("bcc", node->data.s, m)))
  942.         return true;
  943.     return false;
  944.       case SEQ_UNANSWERED:
  945.     return (!(m->flags & M_ANSWERED));
  946.       case SEQ_UNDELETED:
  947.     return (!(m->flags & M_DELETED));
  948.       case SEQ_UNFLAGGED:
  949.     return (!(m->flags & M_FLAGGED));
  950.       case SEQ_UNKEYWORD:
  951.     return(!lookup_keyword(node->data.s, m->keywords));
  952.       case SEQ_UNSEEN:
  953.     return (!(m->flags & M_SEEN));
  954.       case SEQ_RANGE:
  955.     return ((n >= node->data.range.n) &&
  956.         (n <= node->data.range.m));
  957.     }
  958.     ccmd_errmsg ("Unknown sequence constraint type encountered");
  959. }
  960.  
  961.  
  962. #ifdef SEQDEBUG
  963. char *FNS[] = {
  964.     "no function",
  965.     "cmcfm",
  966.     "cmkey",
  967.     "cmnum",
  968.     "cmqst",
  969.     "cmnoi",
  970.     "cmtxt",
  971.     "cmfld",
  972.     "cmswi",
  973.     "cmtok",
  974.     "cmtad",
  975.     "cmfil",
  976.     "cmusr",
  977.     "cmgrp",
  978.     "cmpara"
  979. };
  980.  
  981. printfdb(fdbs)
  982. fdb *fdbs;
  983. {
  984.     if (fdbs == nil) {
  985.     printf ("nil\n");
  986.     return;
  987.     }
  988.     printf ("%s", FNS[fdbs->_cmfnc]);
  989.     if (fdbs->_cmhlp)
  990.     printf (" help='%s'", fdbs->_cmhlp);
  991.     if (fdbs->_cmdef)
  992.     printf (" def='%s'", fdbs->_cmdef);
  993.     printf ("\n");
  994.     printfdb(fdbs->_cmlst);
  995. }
  996.  
  997. seq_print (node,last)
  998. seq_node *node;
  999. int last;
  1000. {
  1001.     int i;
  1002.  
  1003.     for (i = 0; i <= last; i++) {
  1004.     switch (node[i].type) {
  1005.       case SEQ_AFTER:
  1006.         printf ("after %s", daytime (node[i].data.dates.first)); break;
  1007.       case SEQ_ALL:
  1008.         printf ("all"); break;
  1009.       case SEQ_ANSWERED:
  1010.         printf ("answered"); break;
  1011.       case SEQ_BEFORE:
  1012.         printf ("before %s", daytime (node[i].data.dates.first)); break;
  1013.       case SEQ_CURRENT:
  1014.         printf ("current"); break;
  1015.       case SEQ_DELETED:
  1016.         printf ("deleted"); break;
  1017.       case SEQ_FLAGGED:
  1018.         printf ("flagged"); break;
  1019.       case SEQ_FROM:
  1020.         printf ("from \"%s\"", node[i].data.s); break;
  1021.       case SEQ_INVERSE:
  1022.         printf ("inverse"); break;
  1023.       case SEQ_KEYWORD:
  1024.         printf ("keyword"); break;
  1025.       case SEQ_LAST:
  1026.         printf ("last"); break;
  1027.       case SEQ_LONGER:
  1028.         printf ("longer than %d chars", node[i].data.range.n); break;
  1029.       case SEQ_NEW:
  1030.         printf ("new (recent and unseen)"); break;
  1031.       case SEQ_ON:
  1032.         printf ("between %s", daytime (node[i].data.dates.first));
  1033.         printf (" and %s", daytime (node[i].data.dates.last)); break;
  1034.       case SEQ_PREVIOUS:
  1035.         printf ("previous sequence"); break;
  1036.       case SEQ_RECENT:
  1037.         printf ("recent"); break;
  1038.       case SEQ_UNSEEN:
  1039.         printf ("seen");
  1040.       case SEQ_SHORTER:
  1041.         printf ("shorter than %d chars", node[i].data.range.n); break;
  1042.       case SEQ_SINCE:
  1043.         printf ("since %s", daytime (node[i].data.dates.first)); break;
  1044.       case SEQ_SUBJECT:
  1045.         printf ("subject \"%s\"", node[i].data.s); break;
  1046.       case SEQ_TEXT:
  1047.         printf ("text \"%s\"", node[i].data.s); break;
  1048.       case SEQ_TO:
  1049.         printf ("to \"%s\"", node[i].data.s); break;
  1050.       case SEQ_UNANSWERED:
  1051.         printf ("not answered"); break;
  1052.       case SEQ_UNDELETED:
  1053.         printf ("not deleted"); break;
  1054.       case SEQ_UNFLAGGED:
  1055.         printf ("not flagged"); break;
  1056.       case SEQ_UNSEEN:
  1057.         printf ("not seen");
  1058.       case SEQ_RANGE:
  1059.         if (node[i].data.range.n != node[i].data.range.m)
  1060.         printf ("%d:%d", node[i].data.range.n, node[i].data.range.m);
  1061.         else
  1062.         printf ("%d", node[i].data.range.n);
  1063.     }
  1064.     printf ("\n");
  1065.     }
  1066. }
  1067. #endif /*SEQDEBUG*/
  1068.  
  1069.